﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Runtime.Serialization;
using OpenComposite.HumanActivity.Contracts.Participants;

namespace OpenComposite.HumanActivity.Contracts
{
	[DataContract(Name = "Activity", Namespace = "http://www.OpenComposite.com/humanactivities")]
	public class ActivityItem : ItemBase
	{
		#region Initialization
		internal ActivityItem()
		{
			this.Participants = new List<ParticipantItem>();
		}
		internal ActivityItem(DataRow activityInboxRow)
			: this()
		{
			if ( activityInboxRow == null ) return;

			try {
				this.ActivityId = (int)activityInboxRow["activity_id"];
				if ( activityInboxRow.Table.Columns.Contains("workflow_instance_id") &&
					 !activityInboxRow.IsNull("workflow_instance_id") ) {
					this.WorkflowInstanceId = (Guid)activityInboxRow["workflow_instance_id"];
				}
				this.ActivityName = activityInboxRow["activity_nm"] as string;
				this.ProcessName = activityInboxRow["process_nm"] as string;
				this.Subject = activityInboxRow["subject"] as string;
				if ( activityInboxRow.Table.Columns.Contains("hact_id") ) {
					this.HumanActivityId = (int)activityInboxRow["hact_id"];
				}
				if ( activityInboxRow.Table.Columns.Contains("reply_uri") ) {
					this.ReplyUri = activityInboxRow["reply_uri"] as string;
				}
				if ( activityInboxRow.Table.Columns.Contains("message_id") ) {
					this.MessageId = activityInboxRow["message_id"] as string;
				}
				this.ActivityUserId = getNullableInt(activityInboxRow, "activity_user_id");
				this.Created = getNullableDateTime(activityInboxRow, "created_dt");
				this.Started = getNullableDateTime(activityInboxRow, "started_dt");
				this.Due = getNullableDateTime(activityInboxRow, "due_dt");
				this.PostPone = getNullableDateTime(activityInboxRow, "postpone_dt");
				this.Faulted = getNullableDateTime(activityInboxRow, "faulted_dt");
				this.Aborted = getNullableDateTime(activityInboxRow, "aborted_dt");
				this.Completed = getNullableDateTime(activityInboxRow, "completed_dt");
				this.Status = (ActivityStatus)( (int)activityInboxRow["status_id"] );
				if ( activityInboxRow.Table.Columns.Contains("attachment_count") ) {
					this.AttachmentCount = (int)activityInboxRow["attachment_count"];
				}
				if ( activityInboxRow.Table.Columns.Contains("hact_url") ) {
					this.HumanActivityUri = activityInboxRow["hact_url"] as string;
				}
			} catch ( Exception ex ) {
				throw new InvalidOperationException("Invalid activity inbox datarow.", ex);
			}
		}
		internal ActivityItem(Guid workflowInstanceId, string activityName, string processName,
			string subject, string humanActivityUri)
			: this()
		{
			this.WorkflowInstanceId = workflowInstanceId;
			this.ActivityName = activityName;
			this.ProcessName = processName;
			this.Subject = subject;
			this.HumanActivityUri = humanActivityUri;
		}
		#endregion

		#region Data Members
		[DataMember]
		public int? ActivityId { get; private set; }
		[DataMember]
		public Guid WorkflowInstanceId { get; private set; }
		[DataMember]
		public string ActivityName { get; private set; }
		[DataMember]
		public string ProcessName { get; private set; }
		[DataMember]
		public string Subject { get; private set; }
		[DataMember]
		public int? HumanActivityId { get; private set; }
		[DataMember]
		public ActivityStatus? Status { get; private set; }
		[DataMember]
		public string ReplyUri { get; private set; }
		[DataMember]
		public string MessageId { get; private set; }
		[DataMember]
		public int? ActivityUserId { get; private set; }
		[DataMember]
		public DateTime? Created { get; private set; }
		[DataMember]
		public DateTime? Started { get; private set; }
		[DataMember]
		public DateTime? Due { get; private set; }
		[DataMember]
		public DateTime? PostPone { get; private set; }
		[DataMember]
		public DateTime? Faulted { get; private set; }
		[DataMember]
		public DateTime? Aborted { get; private set; }
		[DataMember]
		public DateTime? Completed { get; private set; }
		[DataMember]
		public int AttachmentCount { get; private set; }
		[DataMember]
		public string HumanActivityUri { get; private set; }

		[DataMember]
		public List<ParticipantItem> Participants { get; private set; }
		#endregion

		/// <summary>
		/// Adds a participant.
		/// </summary>
		/// <param name="item">The participant item.</param>
		public void AddParticipant(ParticipantItem item)
		{
			if ( item == null ) return;
			if ( this.Participants == null ) this.Participants = new List<ParticipantItem>();
			if ( !this.Participants.Contains(item) ) {
				this.Participants.Add(item);
			}
		}
		/// <summary>
		/// Adds a participant.
		/// </summary>
		/// <param name="participantType">Type of the participant.</param>
		/// <param name="participantId">The participant id.</param>
		/// <param name="participationType">Type of the participation.</param>
		public void AddParticipant(
			ParticipantType participantType, int participantId, ParticipationType participationType)
		{
			if ( participantType == ParticipantType.None || participantId <= 0 ) return;
			if ( this.Participants == null ) this.Participants = new List<ParticipantItem>();
			ParticipantItem item = new ParticipantItem(participantType, participantId, participationType);
			if ( !this.Participants.Contains(item) ) {
				this.Participants.Add(item);
			}
		}
		/// <summary>
		/// Removes a participant.
		/// </summary>
		/// <param name="item">The participant item.</param>
		public void RemoveParticipant(ParticipantItem item)
		{
			if ( item == null || this.Participants == null ) return;
			if ( this.Participants.Contains(item) ) {
				this.Participants.Remove(item);
			}
		}
		/// <summary>
		/// Removes a participant.
		/// </summary>
		/// <param name="participantType">Type of the participant.</param>
		/// <param name="participantId">The participant id.</param>
		public void RemoveParticipant(ParticipantType participantType, int participantId)
		{
			if ( participantType == ParticipantType.None || participantId <= 0 ) return;
			this.Participants.RemoveAll(
				item => item.ParticipantType == participantType && item.ID == participantId);
		}
		/// <summary>
		/// Gets all participants of a specific participation type.
		/// </summary>
		/// <param name="type">The participation type.</param>
		/// <returns>A list of participant items.</returns>
		public List<ParticipantItem> GetParticipants(ParticipationType type)
		{
			return this.Participants.FindAll(item => item.ParticipationType == type);
		}
	}

	#region Static Class: ActivityItemFactory
	public static class ActivityItemFactory
	{
		#region Static Methods
		public static ActivityItem Create()
		{
			return new ActivityItem();
		}
		public static ActivityItem Create(DataRow row)
		{
			return new ActivityItem(row);
		}
		public static ActivityItem Create(Guid workflowInstanceId, string activityName, string processName,
			string subject, string humanActivityUri)
		{
			return new ActivityItem(
				workflowInstanceId, activityName, processName, subject, humanActivityUri);
		}
		public static List<ActivityItem> CreateList(DataTable table)
		{
			if ( table == null ) throw new ArgumentNullException("table");

			List<ActivityItem> list = new List<ActivityItem>();
			foreach ( DataRow row in table.Rows ) {
				list.Add(Create(row));
			}
			return list;
		}
		#endregion
	}
	#endregion

	#region ActivityItemEventArgs & ActivityItemEventHandler
	public class ActivityItemEventArgs : EventArgs
	{
		public ActivityItemEventArgs(ActivityItem activityItem)
		{
			_acticityItem = activityItem;
		}
		private readonly ActivityItem _acticityItem;
		public ActivityItem ActivityItem { get { return _acticityItem; } }
	}
	public delegate void ActivityItemEventHandler(object sender, ActivityItemEventArgs e);
	#endregion

}
